package com.simpletour.qa.common.repository;

import org.springframework.util.Assert;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;

/**
 * com.simpletour.commons.util.LocalDateTimeUtil <br>
 * 将格林威治时间（0时区）与对应时区时间 转换工具
 *
 * @Description :
 * @Author : tianlei
 * @Create : 2017/12/6.
 * @E-mail : tianlei@simpletour.com
 */
public class Dates {
    /**
     * Date format pattern used to parse date to china date
     * eg. 2017年01月01日
     */
    public static final String PATTERN_CHINESE_DATE = "yyyy年MM月dd日";

    /**
     * Date format pattern used to parse time to china time without second
     * eg. 2017年01月01日 00:00
     */
    public static final String PATTERN_CHINESE_TIME = "yyyy年MM月dd日 HH:mm";

    /**
     * Date format pattern used to parse time to china time
     * eg. 2017年01月01日 00:00:00
     */
    public static final String PATTERN_CHINESE_TIME_FULL = "yyyy年MM月dd日 HH:mm:ss";

    /**
     * Date format pattern used to parse time to general date
     * eg. 2017-01-01
     */
    public static final String PATTERN_GENERAL_DATE = "yyyy-MM-dd";

    /**
     * Date format pattern used to parse time to general time without second
     * eg. 2017-01-01 00:00
     */
    public static final String PATTERN_GENERAL_TIME = "yyyy-MM-dd HH:mm";

    /**
     * Date format pattern used to parse time to general time
     * eg. 2017-01-01 00:00:00
     */
    public static final String PATTERN_GENERAL_TIME_FULL = "yyyy-MM-dd HH:mm:ss";

    /**
     * Date format pattern used to parse time to general date split with slash
     * eg. 2017-01-01
     */
    public static final String PATTERN_GENERAL_SLASH_DATE = "yyyy/MM/dd";

    /**
     * Date format pattern used to parse time to general time without second split with slash
     * eg. 2017-01-01 00:00
     */
    public static final String PATTERN_GENERAL_SLASH_TIME = "yyyy/MM/dd HH:mm";

    /**
     * Date format pattern used to parse time to general time split with slash
     * eg. 2017-01-01 00:00:00
     */
    public static final String PATTERN_GENERAL_SLASH_TIME_FULL = "yyyy/MM/dd HH:mm:ss";

    /**
     * Date format pattern used to parse time to general date split with point
     * eg. 2017-01-01
     */
    public static final String PATTERN_GENERAL_POINT_DATE = "yyyy.MM.dd";

    /**
     * Date format pattern used to parse time to general time without second split with point
     * eg. 2017-01-01 00:00
     */
    public static final String PATTERN_GENERAL_POINT_TIME = "yyyy.MM.dd HH:mm";

    /**
     * Date format pattern used to parse time to general time split with point
     * eg. 2017-01-01 00:00:00
     */
    public static final String PATTERN_GENERAL_POINT_TIME_FULL = "yyyy.MM.dd HH:mm:ss";

    /**
     * eg. 20:12:59
     */
    public static final String PATTERN_TIME = "HH:mm:ss";


    /**
     * Date format pattern used to parse time to general date without separator
     * eg. 20170101
     */
    public static final String PATTERN_DATE_WITHOUT_SEPARATOR = "yyyyMMdd";

    /**
     * Date format pattern used to parse time to general date without separator
     * eg. 20170101120000
     */
    public static final String PATTERN_DATE_TIME_WITHOUT_SEPARATOR = "yyyyMMddHHmmss";

    public static final Long SECONDS_ONE_DAY_L = 86400L;
    public static final Long MILLSECONDS_ONE_DAY_L = 86400_000L;
    public static final Integer SECONDS_ONE_DAY = 86400;
    public static final Integer SECONDS_ONE_HOUR = 3600;
    public static final Integer SECONDS_ONE_MINUTE = 60;

    /**
     * 默认东八区
     */
    public static final String DEFAULT_ZONEID = "+8";

    /**
     * 默认时间偏移
     */
    public static final int DEFAULT_OFFSET_HOURS = 8;

    /**
     * 获取0时区的当前时间
     */
    public static LocalDateTime now() {
        return LocalDateTime.now(ZoneOffset.UTC);
    }

    /**
     * 获取0时区的当前时间
     */
    public static LocalDate today() {
        return LocalDate.now(ZoneOffset.UTC);
    }

    /**
     * 获取0时区的当天0点
     */
    public static LocalDateTime startOfToday() {
        return today().atStartOfDay();
    }

    /**
     * 0时区的epoch秒转换为UTC的LocalDateTime
     */
    public static LocalDateTime fromEpochSecond(Integer epochSecond) {
        Assert.notNull(epochSecond, "epoch second is null");
        return fromEpochSecond(epochSecond.longValue());
    }

    /**
     * 0时区的epoch秒转换为UTC的LocalDateTime
     */
    public static LocalDateTime fromEpochSecond(Long epochSecond) {
        Assert.notNull(epochSecond, "epoch second is null");
        return LocalDateTime.ofEpochSecond(epochSecond, 0, ZoneOffset.UTC);
    }

    /**
     * 获取0时区的某一天天0点
     */
    public static LocalDateTime startOfDay(LocalDate day) {
        Assert.notNull(day, "day is null");
        return day.atStartOfDay(ZoneOffset.UTC).toLocalDateTime();
    }

    /**
     * 把0时区时间转换为对应时区
     *
     * @param original 0时区时间
     * @param offset   要转换的时区的偏移量
     */
    public static LocalDateTime convertByOffset(LocalDateTime original, String offset) {
        Assert.notNull(original, "original is null");
        Assert.hasLength(offset, "offset is null or empty");
        int offsetHours;
        try {
            offsetHours = Integer.parseInt(offset);
        } catch (Exception e) {
            offsetHours = DEFAULT_OFFSET_HOURS;
        }
        return convertByOffset(original, offsetHours);
    }

    /**
     * 把0时区时间转换为对应时区
     *
     * @param original    0时区时间
     * @param offsetHours 要转换的时区的偏移量
     */
    public static LocalDateTime convertByOffset(LocalDateTime original, int offsetHours) {
        Assert.notNull(original, "original is null");
        return LocalDateTime.ofEpochSecond(original.atZone(ZoneOffset.UTC).toEpochSecond(),
                0, ZoneOffset.ofHours(offsetHours));
    }

    /**
     * 把对应时区转换为0时区
     *
     * @param original 某时区时间
     * @param offset   某时区偏移量
     */
    public static LocalDateTime convertToGMT(LocalDateTime original, String offset) {
        Assert.notNull(original, "original is null");
        Assert.hasLength(offset, "offset is null or empty");
        int offsetHours;
        try {
            offsetHours = Integer.parseInt(offset);
        } catch (Exception e) {
            offsetHours = DEFAULT_OFFSET_HOURS;
        }
        return convertToGMT(original, offsetHours);
    }

    /**
     * 把对应时区转换为0时区
     *
     * @param original    某时区时间
     * @param offsetHours 某时区偏移量
     */
    public static LocalDateTime convertToGMT(LocalDateTime original, int offsetHours) {
        Assert.notNull(original, "original is null");
        return LocalDateTime.ofEpochSecond(original.atZone(ZoneOffset.ofHours(offsetHours)).toEpochSecond(),
                0, ZoneOffset.UTC);
    }

    /**
     * 解析LocalDate
     * 默认格式 yyyy-MM-dd
     */
    public static LocalDate parseLocalDate(String day) {
        return parseLocalDate(day, PATTERN_GENERAL_DATE);
    }

    /**
     * 解析LocalDate
     */
    public static LocalDate parseLocalDate(String day, String pattern) {
        Assert.hasLength(day, "day is null or empty");
        Assert.hasLength(pattern, "pattern is null or empty");
        return LocalDate.parse(day, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 解析某时区的LocalDateTime为0时区的时间
     * 默认格式 yyyy-MM-dd HH:mm
     * @param time 某时区的时间字符串
     * @param offset 偏移(即为zoneId)
     */
    public static LocalDateTime parseLocalDateTime(String time,String offset) {
        return parseLocalDateTime(time, PATTERN_GENERAL_TIME,offset);
    }

    /**
     * 解析某时区的LocalDateTime为0时区的时间
     * @param time 某时区的时间字符串
     * @param pattern 格式化的格式
     * @param offset 偏移(即为zoneId)
     * @return
     */
    public static LocalDateTime parseLocalDateTime(String time, String pattern,String offset) {
        Assert.hasLength(time, "time is null or empty");
        Assert.hasLength(pattern, "pattern is null or empty");
        Assert.hasLength(offset, "offset is null or empty");
        return Dates.convertToGMT(LocalDateTime.parse(time, DateTimeFormatter.ofPattern(pattern)), offset);
    }

    /**
     * 格式化LocalDate
     * 默认格式 yyyy-MM-dd
     */
    public static String formatLocalDate(LocalDate localDate) {
        return formatLocalDate(localDate, PATTERN_GENERAL_DATE);
    }

    /**
     * 格式化LocalDate
     */
    public static String formatLocalDate(LocalDate localDate, String pattern) {
        Assert.notNull(localDate, "localDate is null");
        Assert.hasLength(pattern, "pattern is null or empty");
        return localDate.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 格式化LocalDateTime,并且格式化后时间是offset对应时区的时间
     * 默认格式 yyyy-MM-dd HH:mm:ss
     * @param localDateTime 为0时区的时间
     * @param offset 偏移(即为zoneId)
     * @return
     */
    public static String formatLocalDateTime(LocalDateTime localDateTime,String offset) {
        return formatLocalDateTime(localDateTime, PATTERN_GENERAL_TIME_FULL,offset);
    }

    /**
     * 格式化LocalDateTime,并且格式化后时间是offset对应时区的时间
     *  @param localDateTime 为0时区的时间
     * @param pattern 格式化的格式
     * @param offset 偏移(即为zoneId)
     */
    public static String formatLocalDateTime(LocalDateTime localDateTime, String pattern,String offset) {
        Assert.notNull(localDateTime, "localDateTime is null");
        Assert.hasLength(pattern, "pattern is null or empty");
        Assert.hasLength(offset, "offset is null or empty");
        return Dates.convertByOffset(localDateTime, offset).format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * LocalDateTime转换为Integer类型的时间戳（0时区）
     */
    public static Integer toEpochSecond(LocalDateTime localDateTime) {
        return toEpochSecond(localDateTime, ZoneOffset.UTC);
    }

    /**
     * LocalDateTime转换为Integer类型的时间戳
     */
    public static Integer toEpochSecond(LocalDateTime localDateTime, int offsetHours) {
        return toEpochSecond(localDateTime, ZoneOffset.ofHours(offsetHours));
    }

    /**
     * LocalDateTime转换为Integer类型的时间戳
     */
    public static Integer toEpochSecond(LocalDateTime localDateTime, ZoneOffset zoneOffset) {
        Assert.notNull(localDateTime, "localDateTime is null");
        Assert.notNull(zoneOffset, "zoneOffset is null");
        return (int) localDateTime.toEpochSecond(zoneOffset);
    }

    /**
     * @param begin 开始日期，精确到天
     * @param end   结束日期, 精确到天
     * @return 从开始日期到结束日期的日期集合，开始到结束日期以一天依次递增
     */
    public static List<LocalDate> getContinuousDates(LocalDate begin, LocalDate end) {
        Assert.notNull(begin, "begin date is null");
        Assert.notNull(end, "end date is null");
        List<LocalDate> list = new ArrayList<>();
        for (LocalDate i = begin; i.isBefore(end) || i.equals(end); i = i.plusDays(1)) {
            list.add(i);
        }
        return list;
    }

    /**
     * 获取两个日期之间相差的天数
     *
     * @param begin 开始日期，精确到天
     * @param end   结束日期, 精确到天
     * @return 两个日期之间相差的天数
     */
    public static int getDaysBetweenDates(LocalDate begin, LocalDate end) {
        Assert.notNull(begin, "begin date is null");
        Assert.notNull(end, "end date is null");
        return begin.until(end).getDays();
    }

    /**
     * 获取指定日期月份的最大日期（当月最后一天的日期, 忽略时分秒毫秒）
     */
    public static LocalDate getMaxDayOfMonth(LocalDate localDate) {
        Assert.notNull(localDate, "localDate is null");
        return localDate.with(TemporalAdjusters.lastDayOfMonth());
    }

    /**
     * 获取指定日期月份的最小日期（当月第一天日期, 忽略时分秒毫秒）
     */
    public static LocalDate getMinDayOfMonth(LocalDate localDate) {
        Assert.notNull(localDate, "localDate is null");
        return localDate.with(TemporalAdjusters.firstDayOfMonth());
    }

}
